//
// Created by denglibin on 19-10-28.
//
/**
 *poll函数与select函数差不多
 * 　函数原型:
#include <poll.h>
int poll(struct pollfd fd[], nfds_t nfds, int timeout);
 struct pollfd的结构如下：
struct pollfd{
　int fd； // 文件描述符
　short event；// 请求的事件
　short revent；// 返回的事件
}

 每个pollfd结构体指定了一个被监视的文件描述符。第一个参数是一个数组，即poll函数可以监视多个文件描述符。
 每个结构体的events是监视该文件描述符的事件掩码，由用户来设置。revents是文件描述符的操作结果事件，
 内核在调用返回时设置。events中请求的任何事件都可能在revents中返回

 nfds：要监视的描述符的数目。

　　timeout参数指定等待的毫秒数，无论I/O是否准备好，poll都会返回。timeout指定为负数值表示无限超时；timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符，
 但并不等待其它的事件。这种情况下，poll()就像它的名字那样，一旦选举出来，立即返回

 成功时，poll()返回结构体中revents域不为0的文件描述符个数；如果在超时前没有任何事件发生，poll()返回0；失败时，poll()返回-1，并设置errno为下列值之一：
　　EBADF：一个或多个结构体中指定的文件描述符无效。
　　EFAULT：fds指针指向的地址超出进程的地址空间。
　　EINTR：请求的事件之前产生一个信号，调用可以重新发起。
　　EINVAL：nfds参数超出PLIMIT_NOFILE值。
　　ENOMEM：可用内存不足，无法完成请求。


 **/

#include <poll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <errno.h>
#include "wrap.h"

#define  MAX_LINE  4096
#define  SERVER_PORT 8888
#define  OPEN_MAX  1024

static void start_server(){
    int listen_fd, i, max_i, n_ready, conn_fd, sock_fd, n;
    struct pollfd client[OPEN_MAX]; //客户端文件描述符数组
    char buf[MAX_LINE];
    char str[INET_ADDRSTRLEN];

    struct sockaddr_in client_addr, servaddr;
    socklen_t  client_addr_len = sizeof(client_addr);

    listen_fd = Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);

    int  opt = 1;
    setsockopt(listen_fd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    Bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    /*默认最大128*/
    Listen(listen_fd, 20);

    client[0].fd = listen_fd;
    client[0].events = POLLIN;/*监听读事件*/

    for(i = 1; i < OPEN_MAX; i++){
        client[i].fd = -1; // 初始化为-1;
    }
    max_i = 0; //最多多少个客户端

    for(;;){
        n_ready = poll(client, max_i+1, -1);/*阻塞*/
        printf("poll return %d\n", n_ready);
        if(client[0].revents & POLLIN){ // 有新的连接
            conn_fd = Accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);
            printf("new connection %s:%d\n",
                   inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
                   ntohs(client_addr.sin_port));
            /*保存新的连接*/
            for(i = 1; i < OPEN_MAX; i++){
                if(client[i].fd < 0){
                    client[i].fd = conn_fd;
                    break;
                }
            }
            if(i == OPEN_MAX){
                printf("too many clients\n");
                exit(-1);
            }
            /*监听新的客户端的额读事件*/
            client[i].events = POLLIN;
            if(i > max_i){
                max_i = i;
            }
            /*判断是否还有就绪的文件描述符*/
            if(--n_ready <= 0){
                continue;
            }
        }
        printf("max_i = %d\n", max_i);
        for(i = 1; i <= max_i; i++){
            if((sock_fd = client[i].fd) < 0){
                continue;
            }
            printf("sock_fd = %d\n", sock_fd);
            if(client[i].revents & (POLLIN)){
                printf("read from client[%d]\n", i);
                if((n = recv(sock_fd, buf, MAX_LINE, 0)) < 0 ){
                   if(errno == ECONNRESET){/*收到RST标识 连接重置*/
                       printf("client[%d] aborted connection\n", i);
                       Close(sock_fd);
                       client[i].fd = -1;
                   } else {
                       perror("recv");
                       exit(-1);
                   }
                } else if(n == 0){
                    /*断开连接*/
                    printf("client disconnect\n");
                    Close(sock_fd);
                    client[i].fd = -1;
                } else {
                    int j;
                    for(j = 0; j < n; j++){
                        buf[j] = (char)toupper((int)buf[j]);
                    }
                    send(sock_fd, buf, n, 0);
                }
                if(--n_ready <= 0){ // 就绪的文件描述符已经遍历完了
                    break;
                }
            }
        }
    }
}

static int main_(void){

    start_server();
    return 0;
}